/*
 * CMediaFileReader.cpp
 *
 *  Created on: 2013-1-11
 *      Author: terry
 */
#include "stdafx.h"
#include "BasicType.h"
#include "CMediaFileReader.h"
#include <errno.h>

#ifdef WIN32
#else
	#define INVALID_HANDLE_VALUE ((HANDLE)(long)-1)
#endif //



CMediaFileReader::CMediaFileReader():
    m_hFile(INVALID_HANDLE_VALUE),
    m_hMap(),
    m_pView(),
    m_fileSize(),
    m_mediaHeader(),
    m_duration(),
    m_curPos(),
    m_indexTable()
{

}

CMediaFileReader::~CMediaFileReader()
{
}

int CMediaFileReader::open(const char* filename)
{
    m_hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);

    if (INVALID_HANDLE_VALUE == m_hFile)
    {
        return ENOENT;
    }
    
    LARGE_INTEGER filesize;
    filesize.QuadPart = 0;
    GetFileSizeEx(m_hFile, &filesize);
    m_fileSize = filesize.QuadPart;

    if (m_fileSize < sizeof(m_mediaHeader))
    {
        close();
        return EBADF;
    }

    DWORD dwLength = 0;
    ReadFile(m_hFile, &m_mediaHeader, sizeof(m_mediaHeader), &dwLength, NULL);
    if (dwLength != sizeof(m_mediaHeader))
    {
        close();
        return EACCES;
    }
    
    if (!checkFileHeader(m_mediaHeader))
    {
        close();
        return EBADF;
    }

    m_hMap = CreateFileMapping(m_hFile, NULL, PAGE_READONLY, 0, 0, NULL);
    if (m_hMap == NULL)
    {
        CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE;
        return EACCES;
    }

    m_pView = (BYTE*)MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
    m_curPos = sizeof(MediaFileHeader);

    buildIndex();

    return 0;
}

void CMediaFileReader::close()
{
    if (m_pView)
    {
        UnmapViewOfFile(m_pView);
        m_pView = NULL;
    }

    if (m_hMap)
    {
        CloseHandle(m_hMap);
        m_hMap = NULL;
    }

    if (m_hFile != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE;
    }
}

bool CMediaFileReader::isOpen()
{
    return (m_hFile != INVALID_HANDLE_VALUE);
}

const MediaFileHeader& CMediaFileReader::getFileHeader()
{
    return m_mediaHeader;
}

int CMediaFileReader::getDecoderTag()
{
    return m_mediaHeader.decoderTag;
}

int CMediaFileReader::read(unsigned char* buffer, int length, MediaChunkHeader& header)
{
    if (m_curPos >= m_fileSize)
    {
        return ENODATA;
    }

    BYTE* p = m_pView + m_curPos;
    header = *(MediaChunkHeader*)p;
    
    p += sizeof(MediaChunkHeader);

    if ((m_curPos + header.length) > m_fileSize)
    {
        return ENODATA;
    }
    
    if (length > header.length)
    {
        length = header.length;
    }

    memcpy(buffer, p, length);
    m_curPos += sizeof(MediaChunkHeader) + length;

    return 0;
}

int64_t CMediaFileReader::getFileSize()
{
    return m_fileSize;
}

int64_t CMediaFileReader::getDuration()
{
    return m_duration;
}

double CMediaFileReader::getPos()
{
    return (double)m_curPos / m_fileSize;
}

bool CMediaFileReader::setPos(double pos)
{
    if (pos <= 0.0)
    {
        m_curPos = sizeof(MediaFileHeader);
    }
    else if (pos >= 1.0)
    {
        m_curPos = m_fileSize;
    }
    else
    {
        MediaChunkIndex index;
        memset(&index, 0, sizeof(index));
        int64_t offset = (int64_t)(pos * m_fileSize);
        if (m_indexTable.find(offset, index))
        {
            m_curPos = index.offset;
        }
        else
        {
            m_curPos = m_fileSize;
        }
    }
    return true;
}

void CMediaFileReader::buildIndex()
{
    m_indexTable.parseFromFile(m_pView, m_fileSize);

    m_duration = m_indexTable.getDuration();
}

bool CMediaFileReader::checkFileHeader(const MediaFileHeader& header)
{
    return ((header.id[0] == MEDIA_FILE_ID_1) &&
            (header.id[1] == MEDIA_FILE_ID_2) && 
            (header.id[2] == MEDIA_FILE_ID_3) && 
            (header.version == MEDIA_FILE_VERSION)
            );
        
}

bool CMediaFileReader::lookaside(MediaChunkHeader& header)
{
    if (m_curPos >= m_fileSize)
    {
        return false;
    }

    BYTE* p = m_pView + m_curPos;
    header = *(MediaChunkHeader*)p;

    return true;
}
